using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Globalization;
using System.Windows.Forms;
using System.Xml;
using EnvDTE;
using EnvDTE80;
using Microsoft.Practices.ComponentModel;
using Microsoft.Practices.RecipeFramework.Configuration;
using Microsoft.Practices.RecipeFramework.Services;
using VSSDKAssist.Assets.Helpers;

namespace VSShellAssist.Recipes.Common.Coordinators
{
	/// <summary>
	/// Action coordinator that provides logging capabilities
	/// </summary>
	[ServiceDependency(typeof(IActionExecutionService))]
	[ServiceDependency(typeof(DTE))]
	public class ExecutionProgressCoordinator : SitedComponent, IActionCoordinationService
	{
		#region Constants
		private const string StatusMessageAttribute = "StatusMessage";
		private const string WriteMessageToOutputWindowAttribute = "WriteMessageToOutputWindow";
		#endregion

		#region Properties
		private IActionExecutionService executor;

		/// <summary>
		/// Gets the executor.
		/// </summary>
		/// <value>The executor.</value>
		protected IActionExecutionService Executor
		{
			get 
			{
				if(executor == null)
				{
					executor = GetService<IActionExecutionService>(true); ;
				}
				
				return executor; 
			}
		}

		private IConfigurationService configurationService;

		/// <summary>
		/// Gets the configuration service.
		/// </summary>
		/// <value>The configuration service.</value>
		protected IConfigurationService ConfigurationService
		{
			get
			{
				if(configurationService == null)
				{
					configurationService = GetService<IConfigurationService>(true); ;
				}

				return configurationService;
			}
		}

		private IDictionaryService dictionaryService;

		/// <summary>
		/// Gets the dictionary service.
		/// </summary>
		/// <value>The dictionary service.</value>
		protected IDictionaryService DictionaryService
		{
			get
			{
				if(dictionaryService == null)
				{
					dictionaryService = GetService<IDictionaryService>(true); ;
				}

				return dictionaryService;
			}
		}

		private StatusBarHelper statusBarHelper;

		/// <summary>
		/// Gets the status bar helper.
		/// </summary>
		/// <value>The status bar helper.</value>
		protected StatusBarHelper StatusBarHelper
		{
			get
			{
				if(statusBarHelper == null)
				{
					statusBarHelper = new StatusBarHelper(this.Site as IServiceProvider);
				}

				return statusBarHelper;
			}
		}

		private OutputWindowHelper outputWindowHelper;

		/// <summary>
		/// Gets the output window helper.
		/// </summary>
		/// <value>The output window helper.</value>
		protected OutputWindowHelper OutputWindowHelper
		{
			get
			{
				if(outputWindowHelper == null)
				{
					outputWindowHelper =
						new OutputWindowHelper((DTE2)this.GetService<DTE>(true), Properties.Resources.VSShellAssistPane);
				}

				return outputWindowHelper;
			}
		}
		#endregion

		#region IActionCoordinationService Members

		/// <summary>
		/// See <see cref="IActionCoordinationService.Run"/>.
		/// </summary>
		public void Run(Dictionary<string, Action> declaredActions, XmlElement coordinationData)
		{
			Cursor currentCursor = Cursor.Current;

			try
			{
				StatusBarHelper.StartAnimation();
				Cursor.Current = Cursors.WaitCursor;

				foreach(Action action in declaredActions.Values)
				{
					ExecuteAction(action);
				}
			}
			catch(Exception ex)
			{
				OutputWindowHelper.WriteMessage(
					string.Format(
						CultureInfo.CurrentCulture,
						Properties.Resources.ExecutionException,
						ex.Message,
						ex.StackTrace));

				//Re throw to allow GAX to call the Undo on the actions already executed
				throw;
			}
			finally
			{
				Cursor.Current = currentCursor;
				StatusBarHelper.DisplayMessage(Properties.Resources.ReadyStatus);
				StatusBarHelper.StopAnimation();
			}
		}

		#endregion

		#region Protected Implementation
		/// <summary>
		/// Logs the action.
		/// </summary>
		/// <param name="action">The action.</param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")]
		protected void LogAction(Action action)
		{
			Guard.ArgumentNotNull(action, "action");

			XmlAttribute statusMessage;
			XmlAttribute writeMessageToOutputWindow;
			string message;

			if(FindAttribute(action.AnyAttr, StatusMessageAttribute, out statusMessage))
			{
				message = statusMessage.Value;
				StatusBarHelper.DisplayMessage(message);

				if(FindAttribute(action.AnyAttr, WriteMessageToOutputWindowAttribute, out writeMessageToOutputWindow))
				{
					bool writeMessage = false;

					Boolean.TryParse(writeMessageToOutputWindow.Value, out writeMessage);

					if(writeMessage)
					{
						OutputWindowHelper.WriteMessage(
							string.Format(
								CultureInfo.CurrentCulture,
								"{0}: {1}",
								ConfigurationService.CurrentRecipe.Name,
								message));
					}
				}
			}
		}

		/// <summary>
		/// Executes the action.
		/// </summary>
		/// <param name="action">The action.</param>
		protected virtual void ExecuteAction(Action action)
		{
			LogAction(action);
			Executor.Execute(action.Name);
		}

		/// <summary>
		/// Finds the attribute.
		/// </summary>
		/// <param name="attributes">The attributes.</param>
		/// <param name="attributeName">Name of the attribute.</param>
		/// <param name="attributeFound">The attribute found.</param>
		/// <returns></returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#")]
		protected static bool FindAttribute(XmlAttribute[] attributes, string attributeName, out XmlAttribute attributeFound)
		{
			if(attributes != null)
			{
				foreach(XmlAttribute attribute in attributes)
				{
					if(attribute.Name.Equals(attributeName, StringComparison.OrdinalIgnoreCase))
					{
						attributeFound = attribute;
						return true;
					}
				}
			}

			attributeFound = null;
			return false;
		}
		#endregion
	}
}